package typistcal.commandlogic;

import java.util.Stack;

import typistcal.executor.Executor;

//@author A0105620X
public class CommandProcessor {

	private static Stack<Command> undoHistory;
	private static Stack<Command> redoHistory;
	public Command currCommand;
	private boolean isRedo, isUndo, isUndoable;

	public CommandProcessor() {
		if(undoHistory==null){
			undoHistory = new Stack<Command>();
		}
		if(redoHistory==null){
			redoHistory = new Stack<Command>();
		}
		currCommand = null;
	}

	public void createNewCommand(String userInput) {
		assert (userInput != null && userInput.length() != 0);
		isRedo = false;
		isUndo = false;
		isUndoable = false;

		if (CommandTypeKeyword.isUndo(userInput)) {
			isUndo = true;
			updateCurrCommandForUndoAndRedoHistory();
		} else if (CommandTypeKeyword.isRedo(userInput)) {
			isRedo = true;
			updateCurrCommandForRedoAndUndoHistory();
		} else {
			currCommand = new Command(userInput);
			if (currCommand.isUndoable()) {
				isUndoable = true;
				clearRedoHistory();
				undoHistory.push(currCommand);
			}
		}
	}

	private void clearRedoHistory() {
		while (!redoHistory.empty()) {
			redoHistory.pop();
		}
	}

	public StringBuffer executeCurrCommand() {
		if (currCommand == null) {
			return new StringBuffer(
					"Execution failed. No command to undo/redo.");
		} else if (currCommand.commandType == CommandType.INVALID) {
			return new StringBuffer("Failed to decode your command \""
					+ currCommand.getCommandRecord()
					+ "\". Please type \"help\" for assistance.");

		} else {
			try {
				StringBuffer feedback = Executor.executeCommand(currCommand);
				updateInfoOfRedoUndoCommand(feedback);

				if (checkExecutionStatus(feedback)) {
					feedback = updateFeedbackMessage(feedback);
				}
				return feedback.deleteCharAt(0);
			} catch (Exception e) {
				return new StringBuffer("Failed to execute your command \""
						+ currCommand.getCommandRecord()
						+ "\". Please type \"help\" for assistance.");
			}
		}
	}

	private StringBuffer updateFeedbackMessage(StringBuffer feedback) {
		
		switch (currCommand.commandType) {
		case ADD_FOR_REDO:
		case ADD_FOR_UNDO:
		case ADD_INFO:
			return feedback.replace(feedback.indexOf("added")+"added".length(),feedback.length(), "");
		case DELETE_INFO:
		case DELETE_FOR_REDO:
		case DELETE_FOR_UNDO:
			return feedback.replace(feedback.indexOf("deleted")+"deleted".length(),feedback.length(), "");
		case UPDATE_FOR_UNDO:
		case UPDATE_FOR_REDO:
		case UPDATE_INFO:
			return feedback.replace(feedback.toString().toLowerCase().indexOf("successfully")+"successfully".length(),feedback.length(), "");
		default:
			return feedback;
		}		
	}

	private void updateInfoOfRedoUndoCommand(StringBuffer feedback) {
		Command commandToBeUpdated;
		if (isUndoable){
			commandToBeUpdated = undoHistory.pop();
			if(checkExecutionStatus(feedback)){
				 commandToBeUpdated =  commandToBeUpdated.reverseCommandForUndo(feedback.toString());
				undoHistory.push(commandToBeUpdated);
			}
		}else if(isUndo){
			 commandToBeUpdated = redoHistory.pop().reverseCommandForRedo(feedback.toString());
			redoHistory.push( commandToBeUpdated);
		}else if(isRedo){
			 commandToBeUpdated = undoHistory.pop().reverseCommandForUndo(feedback.toString());
			undoHistory.push( commandToBeUpdated);
		}
	}

	private boolean checkExecutionStatus(StringBuffer feedback) {
		char executionStatus = feedback.charAt(0);
		return (executionStatus == '0') ?  false: true;
	}

	private void updateCurrCommandForUndoAndRedoHistory() {
		if (undoHistory.empty()) {
			currCommand = null;
			return;
		} else {
			currCommand = undoHistory.pop();
			redoHistory.push(currCommand);
		}
	}

	private void updateCurrCommandForRedoAndUndoHistory() {
		if (redoHistory.empty()) {
			currCommand = null;
			return;
		} else {
			currCommand = redoHistory.pop();
			undoHistory.push(currCommand);
		}
	}
}
